package com.nf;


import com.alibaba.druid.sql.visitor.functions.Char;
import com.nf.handler.Constants;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

public class MiniBeanProcessor {

    private static final Map<Class<?>,Object> primitiveDefaults=new HashMap<>();

    private final Map<String, String> columnToPropertyOverrides;

    static {
        primitiveDefaults.put(Integer.TYPE,Integer.valueOf(0));
        primitiveDefaults.put(Short.TYPE,Short.valueOf((short) 0));
        primitiveDefaults.put(Byte.TYPE,Byte.valueOf((byte)0));
        primitiveDefaults.put(Float.TYPE,Float.valueOf((0f)));
        primitiveDefaults.put(Double.TYPE,Double.valueOf((0d)));
        primitiveDefaults.put(Long.TYPE,Long.valueOf((0L)));
        primitiveDefaults.put(Boolean.TYPE,Boolean.FALSE);
        primitiveDefaults.put(Character.TYPE,Character.valueOf((char)0));
    }

    private static ServiceLoader<MiniColumnHandler> columnHandlers=ServiceLoader.load(MiniColumnHandler.class);

    public MiniBeanProcessor() {
       this(new HashMap<String, String>());
    }

    public MiniBeanProcessor(Map<String, String> columnToPropertyOverrides) {
        super();
        if (columnToPropertyOverrides==null){
            throw new IllegalArgumentException("columnToPropertyOverrides map cannot be null");
        }
        this.columnToPropertyOverrides=columnToPropertyOverrides;
    }



    public <T> T toBean(ResultSet rs, Class<? extends T> type)throws SQLException{
        //1、实例化Bean的对象
        T bean=newInstance(type);
        //2、利用bean的一系列的setter方法给bean对象赋值
        return populateBean(rs,bean);
    }

    public <T> T newInstance(Class<T> c)throws SQLException{
        T newInstance=null;
        try {
            //调用这个方法来实例化，潜台词就是T类型必须有一个默认构造函数，否则是不能创建出对象的
            newInstance=c.newInstance();
        } catch (InstantiationException e) {
            //捕获了一个反射相关的异常，将其转换成一个SQLException之后重新抛出，让上层代码知道发生了什么错误
            throw new SQLException("Cannot create "+ c.getName()+": "+e.getMessage());
        } catch (IllegalAccessException e) {
            throw new SQLException("Cannot create "+ c.getName()+": "+e.getMessage());
        }
        return newInstance;
    }

    public <T> T populateBean(ResultSet rs,T bean)throws SQLException{
        //ResultSet与属性(PropertyDescriptors)之间的映射关系
        //获取属性描述
        PropertyDescriptor[] props = this.propertyDescriptors(bean.getClass());
        //获取元数据
        ResultSetMetaData metaData = rs.getMetaData();
        //属性与列名之间映射
        int[] ints = mapColumnsToProperties(metaData, props);
        return populateBean(rs,bean,props,ints);
    }

    public PropertyDescriptor[] propertyDescriptors(Class<?> c)throws SQLException{
        BeanInfo beanInfo=null;
        Class clazz;
        try {
            //反射获取bean属性和方法
            beanInfo=Introspector.getBeanInfo(c);
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }
        return beanInfo.getPropertyDescriptors();
    }

    public int[] mapColumnsToProperties(ResultSetMetaData metaData,PropertyDescriptor[]props)throws SQLException{
        //获取列的数目
        int count = metaData.getColumnCount();
        int[] columnToProperty=new int[count+1];
        //填充数据
        Arrays.fill(columnToProperty, Constants.PROPERTY_NOT_FOUND);
        for (int i = 1; i <= count; i++) {
            //先获取属性名
            String columnName = getColumnName(metaData, i);

            String propertyName = columnToPropertyOverrides.get(columnName);
            if (propertyName == null) {
                propertyName=columnName;
            }

            //从实体类中找出同名的bean属性，默认bean属性名=列名
            for(int j=0;j<props.length;j++){
                PropertyDescriptor descriptor = props[j];
//
                //获取列名的下标
                if (propertyName.equals(descriptor.getName())) {
                    columnToProperty[i]=j;
                    //停止内部循环，不要继续遍历，继续外层循环
                    break;
                }
//                if (columnName.equalsIgnoreCase(descriptor.getName()) ||
//                        generousColumnName.equalsIgnoreCase(descriptor.getName())) {
//                    columnToProperty[i] = j;
//                    break;
//                }
            }
        }
        return columnToProperty;
    }

    private String getColumnName(ResultSetMetaData metaData,int i)throws SQLException{
        //先获取别名
        String columnName=metaData.getColumnLabel(i);
        //如果别名为空或名字长度为0
        if (columnName==null||columnName.length()==0) {
            //就获取列名
            columnName=metaData.getColumnName(i);
        }
        return columnName;
    }

    private <T> T populateBean(ResultSet rs,T bean,PropertyDescriptor[] props,int[]columnToProperty)throws SQLException{
        for (int i = 1; i < columnToProperty.length; i++) {
            //从ResultSet结果集中能找到的映射情况，-1表示没有
            int proPos=columnToProperty[i];
            if (proPos== Constants.PROPERTY_NOT_FOUND) {
                //继续下一次循环
                continue;
            }
            PropertyDescriptor descriptor = props[proPos];
            Class<?> propertyType = descriptor.getPropertyType();
            Object value=null;
            if (propertyType!=null) {

//                if (value.equals(String.class)) {
//                    value=rs.getString(i);
//                }else if(value.equals(Integer.class)|| value.equals(Integer.TYPE)){
//                    value=rs.getInt(i);
//                } else if (value.equals(BigDecimal.class)) {
//                    value=rs.getBigDecimal(i);
//                }
                value=this.processColumn(rs,i,propertyType);
                //这里是处理bean的属性为基本类型，
                // 数据库取到的值又是null是，用基本类型来对应的默认值来代替
                if (value == null && propertyType.isPrimitive()) {
                    value=primitiveDefaults.get(propertyType);
                }
                callSetter(bean,descriptor,value);
            }

        }
        return bean;
    }

    private Object processColumn(ResultSet rs,int i,Class<?> propertyType)throws SQLException{
        Object value=rs.getObject(i);
        //如果数据库取到的值是null，并且类型是支持赋值为null的，那么就直接退出，返回null；
        if (value==null && propertyType.isPrimitive()==false) {
            return null;
        }
        //下面数据不为null，再考虑用具体的getXxx方法来获取数据
        for (MiniColumnHandler handler:columnHandlers
             ) {
            if (handler.metach(propertyType)) {
                value=handler.apply(rs,i);
                //结束循环，结束无谓的数据遍历
                break;
            }
        }
        return value;
    }

    private void callSetter(Object bean,PropertyDescriptor prop,Object value){
        Method writeMethod = prop.getWriteMethod();
        try {
            writeMethod.invoke(bean,value);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
